/*
 * Copyright (C) 2012-2022 NXP Semiconductors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "phUwbTypes.h"

#include "phOsalUwb.h"
#include "phUwb_BuildConfig.h"
#include "phNxpLogApis_TmlUwb.h"
#include "phUwbErrorCodes.h"
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <uwb_uwbs_tml_interface.h>

#define PAYLOAD_LEN_MSB 0x02
#define PAYLOAD_LEN_LSB 0x03

#define UCI_RX_HDR_LEN             0x04
#define NORMAL_MODE_LEN_OFFSET     0x03
#define EXTND_LEN_INDICATOR_OFFSET 0x01
#define EXTENDED_LENGTH_OFFSET     0x02

#define UCI_HDR_LEN      0x04
#define HBCI_HEADER_SIZE 0x04

#define EXTND_LEN_INDICATOR_OFFSET_MASK 0x80
#define SR1XX_SET_FWD                   _IOW(0xEA, 0x04, long)

UWBStatus_t uwb_uwbs_tml_init(uwb_uwbs_tml_ctx_t *pCtx)
{
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }

    if (kUWB_bus_Status_OK != uwb_bus_init(&pCtx->busCtx)) {
        return kUWBSTATUS_FAILED;
    }
    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_deinit(uwb_uwbs_tml_ctx_t *pCtx)
{
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }

    if (kUWB_bus_Status_OK != uwb_bus_deinit(&pCtx->busCtx)) {
        return kUWBSTATUS_FAILED;
    }
    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_setmode(uwb_uwbs_tml_ctx_t *pCtx, uwb_uwbs_tml_mode_t mode)
{
    unsigned long protocol = 0;
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }
    pCtx->mode = mode;
    if (mode == kUWB_UWBS_TML_MODE_HBCI) {
        protocol = 1;
    }

    if (ioctl(pCtx->busCtx.spi_fd, SR1XX_SET_FWD, protocol) != 0) {
        perror("query_apps ioctl set");
        return kUWBSTATUS_FAILED;
    }
    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_data_tx(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pBuf, size_t bufLen)
{
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    pCtx->noOfBytesWritten      = -1;

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    if (pBuf == NULL || bufLen == 0) {
        LOG_E("write buffer is Null or bufLen is 0");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    /*pre write delay to allow the device to recover from the last batch of data */
    phOsalUwb_Delay(2);
    bus_status = uwb_bus_data_tx(&pCtx->busCtx, pBuf, bufLen);
    if (bus_status != kUWB_bus_Status_OK) {
        LOG_E("uwb_uwbs_tml_data_tx : uwb_bus_data_tx failed");
        goto end;
    }

    status                 = kUWBSTATUS_SUCCESS;
    pCtx->noOfBytesWritten = bufLen;
end:
    return status;
}

UWBStatus_t uwb_uwbs_tml_data_rx(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pBuf, size_t *pBufLen)
{
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    UWBStatus_t status          = kUWBSTATUS_FAILED;

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    if (pBuf == NULL || pBufLen == NULL) {
        LOG_E("read buffer or bufLen is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    if (pCtx->mode == kUWB_UWBS_TML_MODE_HBCI) {
        size_t payloadLen = 0;
        if (*pBufLen >= HBCI_HEADER_SIZE) {
            bus_status = uwb_bus_data_rx(&pCtx->busCtx, pBuf, HBCI_HEADER_SIZE);
            if (bus_status != kUWB_bus_Status_OK) {
                LOG_E("uwb_bus_data_rx failed");
                goto end;
            }
        }
        else {
            LOG_E("%s Not enough buffer", __FUNCTION__);
            goto end;
        }
        payloadLen = ((pBuf[PAYLOAD_LEN_MSB] << 8) | pBuf[PAYLOAD_LEN_LSB]);
        if (payloadLen != 0) {
            if (*pBufLen >= (HBCI_HEADER_SIZE + payloadLen)) {
                bus_status = uwb_bus_data_rx(&pCtx->busCtx, &pBuf[HBCI_HEADER_SIZE], payloadLen);
                if (bus_status != kUWB_bus_Status_OK) {
                    LOG_E("uwb_bus_data_rx failed");
                    goto end;
                }
            }
            else {
                LOG_E(
                    "%s Not enough buffer %d bytes of buffer is needed", __FUNCTION__, (HBCI_HEADER_SIZE + payloadLen));
                goto end;
            }
        }
        *pBufLen = HBCI_HEADER_SIZE + payloadLen;
    }
    else {
        bus_status = uwb_bus_data_rx(&pCtx->busCtx, pBuf, UCI_HDR_LEN);
        if (bus_status != kUWB_bus_Status_OK) {
            LOG_E("uwb_bus_data_rx failed");
            goto end;
        }
        *pBufLen = pCtx->busCtx.bytesRead;
    }
    status = kUWBSTATUS_SUCCESS;
    uwb_port_DelayinMicroSec(50);
end:
    return status;
}

UWBStatus_t uwb_uwbs_tml_data_trx(
    uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pTxBuf, size_t txBufLen, uint8_t *pRxBuf, size_t *pRxBufLen)
{
    if (pCtx->mode = kUWB_UWBS_TML_MODE_HBCI) {
        LOG_MAU8_D("HBCI TX: ", pTxBuf, txBufLen);
        if (uwb_uwbs_tml_data_tx(pCtx, pTxBuf, txBufLen) != UWBSTATUS_SUCCESS) {
            LOG_E("uwb_uwbs_tml_data_trx : uwb_uwbs_tml_data_tx failed");
            return UWBSTATUS_FAILED;
        }
        if (uwb_uwbs_tml_data_rx(pCtx, pRxBuf, pRxBufLen) != UWBSTATUS_SUCCESS) {
            LOG_E("uwb_uwbs_tml_data_trx : uwb_uwbs_tml_data_rx failed");
            return UWBSTATUS_FAILED;
        }
        LOG_MAU8_D("HBCI RX: ", pRxBuf, *pRxBufLen);
        uwb_port_DelayinMicroSec(50);
    }
    else {
        /* Not needed for other mode */
    }
    return UWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_helios_hardreset(uwb_uwbs_tml_ctx_t *pCtx)
{
    /* Dummy function. Not needed here. */
    return UWBSTATUS_SUCCESS;
}